/*
Copyright 2010  Murali Devi (profdevi@gmail.com)

This file is part of ComineGL Project.

ComineGL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

ComineGL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with ComineGL.  If not, see <http://www.gnu.org/licenses/>.

This code uses the irrlicht and irrklang libraries.  Please read
the libraries copyrights at 

	irrlicht library:  http://irrlicht.sourceforge.net
	irrklang library:  http://www.ambiera.com/irrklang

*/



//v0.14 copyright Comine.com 20101212U01
#include <stdarg.h>
#include "MStdLib.h"
#include "Global.h"
#include "ComineGLAnimation.h"


//***********************************************
//** Module Elements
//***********************************************

////////////////////////////////////////////////////////
struct GExtraAnimationInfo
	{
	int EventID;										// Event ID 
	int NodeID;											// Node on which animation is playing
	TRefCount<MCGLAnimationInfo> *AnimationInfo;			// Animation info
	TRefCount<MCGLNodeInfo> *NodeInfo;					// Node Associated with animation
	int CurrentFrameIndex;								// Get Current Frame Displayed
	bool LoopAnimation;									// Loop the animation
	bool AutoHideNode;									// =true if node should be auto hidden
	};


/////////////////////////////////////////////////////////
static bool GOnAnimationTask(MTimeEventQueue &manager,int eventid,void *extra)
	{
	MStdAssert(extra!=NULL);
	GExtraAnimationInfo *extraanimationinfo=(GExtraAnimationInfo *)extra;
	MStdAssert(extraanimationinfo!=NULL);
	
	// Reference the animinfo/nodeinfo
	MCGLAnimationInfo *animinfo=(MCGLAnimationInfo *)extraanimationinfo->AnimationInfo;
	MCGLNodeInfo *nodeinfo=(MCGLNodeInfo *)extraanimationinfo->NodeInfo;

	// Increase the Current Frame
	extraanimationinfo->CurrentFrameIndex = extraanimationinfo->CurrentFrameIndex + 1;

	ISceneNode *inode=nodeinfo->GetNode();
	if(extraanimationinfo->CurrentFrameIndex>=animinfo->GetCount() &&
			extraanimationinfo->LoopAnimation==false)
		{
		if(extraanimationinfo->AutoHideNode==true)
			{
			inode->setVisible(false);
			inode->updateAbsolutePosition();
			}

		return false;
		}
	
	//=we are in a regular animation sequence

	inode->setMaterialTexture(0,animinfo->Get(extraanimationinfo->CurrentFrameIndex) );

	return true;
	}


/////////////////////////////////////////////////////////
static bool GOnAnimationTaskEnd(MTimeEventQueue &manager,int eventid,void *extra)
	{
	MStdAssert(extra!=NULL);
	GExtraAnimationInfo *extraanimationinfo=(GExtraAnimationInfo *)extra;
	MStdAssert(extraanimationinfo!=NULL);
	
	// Drop Reference to Animation 
	MStdAssert(extraanimationinfo->AnimationInfo!=NULL);
	extraanimationinfo->AnimationInfo->Drop();
	extraanimationinfo->AnimationInfo=NULL;

	// Drop Reference to Node
	MStdAssert(extraanimationinfo->NodeInfo!=NULL);
	extraanimationinfo->NodeInfo->Drop();
	extraanimationinfo->NodeInfo=NULL;
	
	// Release extra information
	delete extraanimationinfo;
	return true;
	}


//***********************************************
//** Exported Functions
//***********************************************
MDLLFUN bool CGLAnimationLoad(int animid,const char *filename,int rows,int cols,int deltatime,bool rowmajor)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	// Remove old animation
	if(GlobalAnimationList.IsKey(animid)==true)
		{
		GlobalAnimationList.Remove(animid);
		}

	TRefCount<MCGLAnimationInfo> *newanim=new TRefCount<MCGLAnimationInfo>;
	if(newanim==NULL)
		{
		return false;
		}

	if(newanim->Create(GlobalRefVideoDriver,filename,rows,cols,deltatime,rowmajor)==false)
		{
		newanim->Drop();
		MStdPrintf("**Unable to load animation file %s\n",filename);
		return false;
		}
	
	if(GlobalAnimationList.Add(animid,newanim)==false)
		{
		newanim->Drop();
		return false;
		}

	// Drop the reference
	newanim->Drop();
	
	MStdPrintf("Loaded Animation %d\n",animid);
	return true;
	}


///////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationExists(int fontid)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	return GlobalAnimationList.IsKey(fontid);
	}


///////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationPlay(int animid,int nodeid,bool loop,bool autohidenode)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	// Find animation src
	TRefCount<MCGLAnimationInfo> *animinfo=GlobalAnimationList.Find(animid);
	if(animinfo==NULL)
		{
		MStdPrintf("**Unable to find animation %d\n",animid);
		return false;
		}
	
	// Find node target
	TRefCount<MCGLNodeInfo> *nodeinfo=GlobalNodeList.Find(nodeid);
	if(nodeinfo==NULL)
		{
		MStdPrintf("**Unable to find node %d\n",nodeid);
		return false;
		}

	// Allocate extra information
	GExtraAnimationInfo *extrainfo=new GExtraAnimationInfo;
	if(extrainfo==NULL)
		{
		MStdPrintf("**Unable to alloc memory\n",__LINE__);
		return false;
		}

	// Increase interal eventid by 1 for tracking internals tasks
	GlobalInternalEventID = GlobalInternalEventID + 1;

	// Init Data members of extra information
	extrainfo->EventID=GlobalInternalEventID;
	extrainfo->NodeID=nodeid;
	extrainfo->AnimationInfo=animinfo;
	extrainfo->NodeInfo=nodeinfo;
	extrainfo->CurrentFrameIndex=0;
	extrainfo->LoopAnimation=loop;

	ISceneNode *inode=nodeinfo->GetNode();
	extrainfo->AutoHideNode=autohidenode;

	// increase ref counts
	animinfo->Grab();
	nodeinfo->Grab();

	// Add the event
	if(GlobalTaskQueueInternal.EventAdd(GlobalInternalEventID,true,animinfo->GetDeltaTime()
			,GOnAnimationTask,GOnAnimationTaskEnd,extrainfo)==false)
		{
		// Clearup failure
		animinfo->Drop();
		nodeinfo->Drop();
		extrainfo->AnimationInfo=NULL;
		extrainfo->NodeInfo=NULL;
		delete extrainfo;
		extrainfo=NULL;
		MStdPrintf("**Unable to add a new internal task\n");
		return false;
		}


	// Start Playing the first animation texture
	inode->setMaterialTexture(0,animinfo->Get(0) );

	// Make Sure node is visible
	inode->setVisible(true);
	inode->updateAbsolutePosition();
	
	return true;
	}


/////////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationStop(int nodeid)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	MPtrList extralist;
	if(GlobalTaskQueueInternal.GetEventsExtra(extralist)==false)
		{
		return false;
		}

	// Go Through the eventlist looking for the node
	extralist.ReadReset();

	void *extrainfo;
	while(extralist.Read(extrainfo)==true)
		{
		GExtraAnimationInfo *animinfo=(GExtraAnimationInfo *)extrainfo;
		if(animinfo->NodeID==nodeid)
			{
			GlobalTaskQueueInternal.EventCancel(animinfo->EventID);
			}
		}

	MStdPrintf("Cancelled all animations on node %d\n",nodeid);
	return true;	
	}


/////////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationColorKey(int animid,int alpha,int red,int green,int blue)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	// Find animation src
	TRefCount<MCGLAnimationInfo> *animinfo=GlobalAnimationList.Find(animid);
	if(animinfo==NULL)
		{
		MStdPrintf("**Unable to find animation %d\n",animid);
		return false;
		}
	
	int i;
	for(i=0;i<animinfo->GetCount();++i)
		{
		if(animinfo->Get(i)==NULL)
			{
			continue;
			}

		GlobalRefVideoDriver->makeColorKeyTexture(animinfo->Get(i),SColor(alpha,red,green,blue) );
		animinfo->Get(i)->regenerateMipMapLevels();
		}
	
	return true;	
	}


/////////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationColorKeyPixel(int animid,int pixelx,int pixely)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	TRefCount<MCGLAnimationInfo> *animinfo=GlobalAnimationList.Find(animid);
	if(animinfo==NULL)
		{
		MStdPrintf("**Unable to find animation %d\n",animid);
		return false;
		}

	const int framecount=animinfo->GetCount();
	int i;
	for(i=0;i<framecount;++i)
		{
		if(animinfo->Get(i)==NULL)
			{
			continue;
			}

		GlobalRefVideoDriver->makeColorKeyTexture(animinfo->Get(i),position2d<int>(pixelx,pixely) );
		animinfo->Get(i)->regenerateMipMapLevels();
		}

	return true;
	}


///////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationUnload(int animid)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	if(GlobalAnimationList.Remove(animid)==false)
		{
		MStdPrintf("**Unable to unload animation %d\n",animid);
		return false;
		}

	return true;	
	}


//////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimationUnloadAll(void)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}

	if(GlobalAnimationList.Clear()==false)
		{
		MStdPrintf("**Unable to clear all the animations",__LINE__);
		return false;
		}	
	
	return true;
	}


//////////////////////////////////////////////////////////
MDLLFUN int CGLAnimationGetCount(void)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return 0;
		}
	
	return GlobalAnimationList.GetCount();
	}

